﻿#region File Header
//-----------------------------------------------------------------------------
// SibLib
//
// Copyright (C) 2010 Julien Villers
// This program is distributed under the terms of the 
// GNU Lesser General Public License (LGPL).
// See Docs/lgpl.txt and Docs/gpl.txt
//
// Based on a XNAWiki sample.
//-----------------------------------------------------------------------------

#endregion
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace SibLib.Text
{
    /// <summary>
    /// Represents formatted text with inline button glyphs, clipping and new lines.
    /// </summary>
    public class TextDrawer
    {
        private SpriteFont m_Font, m_ButtonFont;
        private Dictionary<string, string> m_ButtonGlyphs = new Dictionary<string, string>();
        private float m_LineHeight = 0;

        public TextDrawer()
        {
            ButtonScale = 0.6f;
            Position = Vector2.Zero;
            Text = "Here is some example text. Press [A] or [START] to continue.";
            Color = Color.Black;
            Size = Vector2.Zero;
            ButtonScale = 0.6f;
            ClipText = false;
        }

        #region Properties
        private string m_Text;
        public string Text
        {
            get { return m_Text; }
            set
            {
                m_Text = value;
                if (m_Size != Vector2.Zero && !String.IsNullOrEmpty(m_Text))
                    WrapText();
            }
        }
        public bool ClipText
        {
            get;
            set;
        }
        public Vector2 Position
        {
            get;
            set;
        }
        public Color Color
        {
            get;
            set;
        }
        private Vector2 m_Size;
        public Vector2 Size
        {
            get { return m_Size; }
            set
            {
                m_Size = value;
                if (m_Size != Vector2.Zero)
                    WrapText();
            }
        }
        public float ButtonScale
        {
            get;
            set;
        }
        #endregion

        public void LoadContent(ContentManager content, GraphicsDevice device)
        {
            m_ButtonGlyphs = new Dictionary<string, string>();
            m_ButtonGlyphs.Add("[LTHUMB]", " ");
            m_ButtonGlyphs.Add("[RTHUMB]", "\"");
            m_ButtonGlyphs.Add("[DPAD]", "!");
            m_ButtonGlyphs.Add("[BACK]", "#");
            m_ButtonGlyphs.Add("[GUIDE]", "$");
            m_ButtonGlyphs.Add("[START]", "%");
            m_ButtonGlyphs.Add("[X]", "&");
            m_ButtonGlyphs.Add("[Y]", "(");
            m_ButtonGlyphs.Add("[A]", "'");
            m_ButtonGlyphs.Add("[B]", ")");
            m_ButtonGlyphs.Add("[LB]", "-");
            m_ButtonGlyphs.Add("[RB]", "*");
            m_ButtonGlyphs.Add("[RT]", "+");
            m_ButtonGlyphs.Add("[LT]", ",");

            m_Font = content.Load<SpriteFont>("Font");
            m_ButtonFont = content.Load<SpriteFont>("xboxControllerSpriteFont");
            WrapText();
        }

        public void Draw(SpriteBatch sb)
        {
            m_LineHeight = m_Font.LineSpacing;
            if (ClipText)
            {
                sb.GraphicsDevice.RenderState.ScissorTestEnable = true;
                sb.GraphicsDevice.ScissorRectangle = new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
            }
            DrawText(Text, sb, Position);
            if (ClipText)
            {
                sb.End();
                sb.Begin();
                sb.GraphicsDevice.RenderState.ScissorTestEnable = false;
            }
        }

        private void DrawText(string text, SpriteBatch sb, Vector2 position)
        {
            int tagStart = text.IndexOf('[');
            int tagEnd = text.IndexOf(']', tagStart != -1 ? tagStart : 0);
            if (tagStart > 0)
                position += DrawString(text.Substring(0, tagStart), sb, position);
            if (tagStart != -1 && tagEnd != -1)
                position += DrawButton(text.Substring(tagStart, tagEnd - tagStart + 1), sb, position);
            if (tagEnd != -1)
                DrawText(text.Substring(tagEnd + 1), sb, position);
            else
                DrawString(text, sb, position);
        }

        private Vector2 DrawString(string text, SpriteBatch sb, Vector2 position)
        {
            sb.DrawString(m_Font, text, position, Color);
            return new Vector2(m_Font.MeasureString(text).X, 0);
        }

        private Vector2 DrawButton(string tag, SpriteBatch sb, Vector2 position)
        {
            //Convert tag into button font text
            if (m_ButtonGlyphs.ContainsKey(tag))
                tag = m_ButtonGlyphs[tag];
            else if (tag == "[NL]")
            {
                Vector2 r = new Vector2(Position.X - position.X, m_LineHeight);
                m_LineHeight = m_Font.LineSpacing;
                return r;
            }
            else
                return DrawString(tag, sb, position);

            Vector2 size = m_ButtonFont.MeasureString(tag) * ButtonScale;
            if (m_LineHeight < size.Y * 0.5f) m_LineHeight = size.Y * 0.4f;
            //Draw string
            sb.DrawString(m_ButtonFont, tag, position, Color.White, 0f, new Vector2(0, size.Y / 2), ButtonScale, SpriteEffects.None, 0);
            return new Vector2(size.X, 0);
        }

        private void WrapText()
        {
            if (m_Font == null || Size == Vector2.Zero) 
                return;

            string[] words = Text.Split(' ');
            StringBuilder sb = new StringBuilder();

            float lineWidth = 0f;
            float spaceWidth = m_Font.MeasureString(" ").X;

            foreach (string word in words)
            {
                float size = MeasureWord(word);
                if (lineWidth + size > Size.X)
                {
                    sb.Append(word + "[NL]");
                    lineWidth = 0f;
                }
                else
                {
                    sb.Append(word);
                    lineWidth += size;
                }
            }
            m_Text = sb.ToString();
        }

        private float MeasureWord(string text)
        {
            float length = 0f;
            int startIndex = text.IndexOf('[');
            int endIndex = text.IndexOf(']', startIndex != -1 ? startIndex : 0);
            if (startIndex > 0)
                length = m_Font.MeasureString(text.Substring(0, startIndex)).X;
            if (startIndex != -1 && endIndex != -1)
                length += MeasureTag(text.Substring(startIndex, endIndex - startIndex + 1));
            if (endIndex != -1)
                length += MeasureWord(text.Substring(endIndex + 1));
            else
                return m_Font.MeasureString(text).X;
            if (length < 0) length = 0;
            return length;
        }

        private float MeasureTag(string tag)
        {
            if (m_ButtonGlyphs.ContainsKey(tag))
                return m_ButtonFont.MeasureString(m_ButtonGlyphs[tag]).X;
            else if (tag == "[NL]")
                return float.NegativeInfinity;
            else
                return m_Font.MeasureString(tag).X;
        }
    }
}
